Autor: Nandico Aquino
Repositório Github: https://github.com/nandico/pm-exploration-challenge
(Privado: Por favor solicitar acesso pelo nandico@gmail.com)
Utilize o link a seguir para pular para o case:
import sys
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as mtick
from datetime import datetime
import nltk
import string
from wordcloud import WordCloud
from IPython.core.display import display, HTML
def prepare_segmented_funnel(leads_df, phase_column_name, segment_column_name):
leads_filtered_df = leads_df[[phase_column_name, segment_column_name]].sort_values(by=[phase_column_name])
leads_filtered_dg = leads_filtered_df.groupby([phase_column_name, segment_column_name])
leads_count_df = leads_filtered_dg[[segment_column_name]].count()
phases_sr = pd.Series(leads_df[phase_column_name].unique()).sort_values()
transposed_leads_count_df = pd.DataFrame()
for item, value in phases_sr.iteritems():
transposed_leads_count_df = transposed_leads_count_df.append(
leads_count_df.loc[value].transpose().rename(index={segment_column_name:value}),
sort=True
)
transposed_leads_count_df.fillna(0, inplace=True)
return transposed_leads_count_df
def relativize_total_funnel(funnel_df):
rel_funnel_df = funnel_df.copy()
cols = list(rel_funnel_df)
for col in cols:
max_value = rel_funnel_df[col].max()
rel_funnel_df[col] = rel_funnel_df[col].apply(lambda x:x/max_value)
return rel_funnel_df
def relativize_previous_step_funnel_loss(funnel_df):
rel_funnel_df = funnel_df.copy()
cols = list(rel_funnel_df)
for col in cols:
rel_funnel_df[col] = 1.0 - process_funnel_series_by_step(rel_funnel_df[col])
return rel_funnel_df
def relativize_previous_step_funnel_retention(funnel_df):
rel_funnel_df = funnel_df.copy()
cols = list(rel_funnel_df)
for col in cols:
rel_funnel_df[col] = process_funnel_series_by_step(rel_funnel_df[col])
return rel_funnel_df
def process_funnel_series_by_step(funnel_sr):
funnel_ls = list(funnel_sr)
funnel_new_ls = funnel_ls.copy()
for i, val in enumerate(funnel_ls):
if i > 0:
previous_value = funnel_ls[i - 1]
else:
previous_value = 0
if previous_value != 0:
funnel_new_ls[i] = funnel_ls[i] / previous_value
else:
funnel_new_ls[i] = 0
return pd.Series(funnel_new_ls, index=funnel_sr.keys())
def display_segmented_funnels(leads_df, phase_column_name, segment_column_name):
funnel_df = prepare_segmented_funnel(leads_df, phase_column_name, segment_column_name)
display(HTML('<h5>{} - distribuição bruta</h5>'.format(segment_column_name)));
display(funnel_df)
funnel_percent_total_df = relativize_total_funnel(funnel_df)
display(HTML('<h5>{} - percentual de retenção em relação à boca do funil</h5>'.format(segment_column_name)));
display(funnel_percent_total_df)
funnel_percent_step_retention_df = relativize_previous_step_funnel_retention(funnel_df)
display(HTML('<h5>{} - percentual de retenção em relação ao passo anterior</h5>'.format(segment_column_name)));
display(funnel_percent_step_retention_df)
funnel_percent_step_loss_df = relativize_previous_step_funnel_loss(funnel_df)
display(HTML('<h5>{} - percentual de perda em relação ao passo anterior</h5>'.format(segment_column_name)));
display(funnel_percent_step_loss_df)
return [funnel_df, funnel_percent_total_df, funnel_percent_step_retention_df, funnel_percent_step_loss_df]
orders_na = [
' - '
]
orders_df = pd.read_excel('../data/Case.xlsx', thousands=',', na_values=orders_na, verbose=False, sheet_name=0)
questions_df = pd.read_excel('../data/Case.xlsx', thousands=',', sheet_name=1)
Descrição dos campos e tipos de dados da base de dados de ordens:
orders_df.dtypes
Amostra da base de dados de ordens (primeiros registros):
orders_df.head()
Sumário estatístico da base de ordens (tendência central, dispersão e formato do dataset):
orders_df.describe()
orders_df['Etapa de checkout'].replace(
to_replace=['Iniciado', 'Registrado', 'Comprometido', 'Pago'],
value=['A: Iniciado', 'B: Registrado', 'C: Comprometido', 'D: Pago'],
inplace=True
)
corr = orders_df.corr()
corr.style.background_gradient(cmap='coolwarm')
orders_df['Etapa de checkout'].value_counts(sort=True, ascending=True).plot.barh();
filtered_refferal_orders_df = orders_df[
(orders_df['Como conheceu o QB'] == 'amigo') |
(orders_df['Como conheceu o QB'] == 'email') |
(orders_df['Como conheceu o QB'] == 'facebook') |
(orders_df['Como conheceu o QB'] == 'faculdade') |
(orders_df['Como conheceu o QB'] == 'google')
]
display_segmented_funnels(filtered_refferal_orders_df, 'Etapa de checkout', 'Como conheceu o QB');
filtered_payment_orders_df = orders_df[orders_df['Tipo do Pagamento'].notnull()]
display_segmented_funnels(filtered_payment_orders_df, 'Etapa de checkout', 'Tipo do Pagamento');
display_segmented_funnels(orders_df, 'Etapa de checkout', 'Modalidade do curso');
A filtragem por nome do curso necessita da criação de um dicionário de sinônimos para nome do curso, o que não é viável pelo tempo da análise.
Informações de valor de desconto anotadas na base apenas para as etapas C: Comprometido e D: Pago.
filtered_level_orders_df = orders_df[
(orders_df['Nível do curso'] == 'Bacharelado (graduação)') |
(orders_df['Nível do curso'] == 'Graduação') |
(orders_df['Nível do curso'] == 'Licenciatura (graduação)') |
(orders_df['Nível do curso'] == 'Pós-graduação Lato Sensu') |
(orders_df['Nível do curso'] == 'Tecnólogo (graduação)')
]
display_segmented_funnels(filtered_level_orders_df, 'Etapa de checkout', 'Nível do curso');
display_segmented_funnels(filtered_level_orders_df, 'Etapa de checkout', 'Turno do curso');
filtered_campus_state_df = orders_df[
(orders_df['Estado do campus'] == 'SP') |
(orders_df['Estado do campus'] == 'RJ') |
(orders_df['Estado do campus'] == 'AM') |
(orders_df['Estado do campus'] == 'DF') |
(orders_df['Estado do campus'] == 'MG') |
(orders_df['Estado do campus'] == 'PE') |
(orders_df['Estado do campus'] == 'BA')
]
display_segmented_funnels(filtered_campus_state_df, 'Etapa de checkout', 'Estado do campus');
display_segmented_funnels(
orders_df[orders_df['Ano que prestou ENEM'] > 0],
'Etapa de checkout',
'Ano que prestou ENEM'
);
bins=30
plt.figure(
figsize=(15,8)
)
plt.hist(
orders_df['Preço cheio no comprovante'].dropna().clip(lower=100, upper=2000),
bins=bins,
alpha=0.5,
rwidth=0.8,
label='Preço cheio'
)
plt.hist(
orders_df['Preço com desconto no comprovante'].dropna().clip(lower=100, upper=2000),
bins=bins,
alpha=0.5,
rwidth=0.8,
label='Preço com desconto'
)
plt.title('Distribuição por preço cheio / preço com desconto')
plt.legend(loc='upper right')
plt.show()
Descrição dos campos e tipos de dados da base de dados de questões:
questions_df.dtypes
Amostra da base de dados de ordens (quatro primeiros registros):
questions_df.head()
print("Total de dúvidas na base: {}".format(len(questions_df)))
questions_df.drop_duplicates(inplace=True)
Remoção de registros duplicados:
print("Total de dúvidas na base: {}".format(len(questions_df)))
Sete principais cursos entre os usuários da amostra:
questions_df['Curso'].value_counts(sort=True, ascending=False)[0:10].plot.bar(figsize=[10,5]);
Dez principais universidades entre os usuários da amostra:
questions_df['Universidade'].value_counts(sort=True, ascending=False)[0:10].plot.bar(figsize=[10,5]);
nltk.download('stopwords');
stopwords_pt_nltk = nltk.corpus.stopwords.words('portuguese')
with open("stopwords.txt", 'r', encoding='utf-8') as f:
stopwords_pt_custom_raw = f.readlines()
stopwords_pt_custom = list(map(lambda x: x.strip('\n').strip(), stopwords_pt_custom_raw))
def remove_stopwords(text):
global stopwords_pt_nltk
global stopwords_pt_custom
text = text.translate(str.maketrans('', '', string.punctuation)).lower()
filtered = []
for word in text.split():
if word not in stopwords_pt_nltk:
if word not in stopwords_pt_custom:
filtered.append(word)
return ' '.join(filtered);
questions_df["Pergunta NSW"] = questions_df["Pergunta"].apply(remove_stopwords)
questions_words = ' '.join(questions_df["Pergunta NSW"].tolist())
questions_pure_words = ' '.join(questions_df["Pergunta"].tolist())
wordcloud = WordCloud(
background_color='white',
max_words=500
).generate(questions_words)
plt.figure(
dpi=300,
figsize=(15,8)
)
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.title('Palavras')
plt.show()
from nltk import tokenize
from nltk.text import Text
nltk.download('punkt');
tokens = tokenize.word_tokenize(questions_words)
pure_tokens = tokenize.word_tokenize(questions_pure_words)
text = Text(tokens, name="Questões dos clientes")
pure_text = Text(pure_tokens, name="Questões dos clientes (Brutas)")
Colocações no NLTK são expressões de múltiplas palavras que mais comumente ocorrem no texto.
Colocações mais importantes a partir do texto com stopwords removidas:
text.collocations()
Colocações mais importantes a partir do texto original:
pure_text.collocations()
plt.figure(figsize=(12,6))
text.vocab().plot(30, cumulative=False)
frequency_df = pd.DataFrame.from_dict(text.vocab(), orient='index', columns=['count'])
frequency_df.sort_values(by=['count'], ascending=False, inplace=True)
for idx in frequency_df[frequency_df["count"] > 5].index:
display(HTML('<h5>{}</h5>'.format(idx)));
pure_text.concordance(idx);
Olá,
Meu nome é Nandico Aquino. Sou empreendedor e gerente de produto.
Durante os últimos vinte anos eu trabalhei com produtos digitais atendendo a empresas como Fiat Chrysler Automobiles, Renault, Nestlé, Whirpool, Pepsico, Unilever, HBO e Weather Channel.
Possuo um histórico de habilidades técnicas com capacidades atualizadas em análise de dados para gestão de produto, desenvolvimento web e desenvolvimento móvel.
Dediquei os anos mais recentes de minha carreira trabalhando em atividades associadas à gestão de produto (product management), trabalhando com pessoas de excelência técnica mundial em design e programação.
| **Github:** | https://github.com/nandico |
| **Twitter:** | https://twitter.com/nandico |
Este trabalho é uma resposta ao Quero Challenge: Desafio de recrutamento da Quero Educação voltado para seleção para a vaga de Group Product Manager.
O descritivo do desafio foi enviado por Letícia Rosas através do link: https://drive.google.com/file/d/1fB8mZopweZbIXBc7joKsZfRrg2J12THi/view?usp=sharing
O objetivo central do desafio é analisar dois conjuntos de dados amostrais distintos e realizar análise heurística do canal https://querobolsa.com.br/ para falar sobre questões estratégicas do produto Quero Bolsa.
Minha ferramenta de escolha para a resolução do case foi o Jupyter Notebook, uma aplicação web open source que permtie criar e compartilhar documentos contendo código de programação, equações, visualizações e narrativas de texto.
A solução pelo Jupyter apresenta como pontos positivos:
A exploração das narrativas podem variar em profundidade por conta da amplitude do problema do case versus a restrição de tempo de análise (5 dias corridos em tempo extra).
A anatomia de funil usada pelo time do Quero Bolsa possui apenas quatro passos, o que oferece praticidade e possibilidade de unificação de vocabulário e abordagem de métricas entre todo o time. As etapas são descritas no material original do case.
A distribuição atual da base de amostra se divide assim:
orders_df['Etapa de checkout'].value_counts(sort=True, ascending=True).plot.barh();
Em observação simples, é possível observar a forte atuação dos gargalos das etapas B: Registrado para C:Comprometido, e também de C:Comprometido para D: Pago. A existência de taxa de perda perto de 50% em relação a etapa anterior demonstra espaço para atuações de melhoria.
Para a análise da origem do lead a base foi separada nas fontes de acesso mais relevantes:
A base possui atualmente uma anomalia provavelmente decorrente da sequência de preenchimento das informações ao longo do funil: na evolução dos passos, em muitas dimensões, a forma de funil é quebrada. Provavelmente a informação de 'Como você conheceu o QB' é preenchida apenas em passos posteriores, o que vai contaminando a forma conforme a segmentação buscada.
segmented_refferal_funnels = display_segmented_funnels(
filtered_refferal_orders_df,
'Etapa de checkout',
'Como conheceu o QB'
);
B: Registrado para C: Comprometido¶ℹ️ Taxa de perda mais alta para indicações de amigo (47.34%).
ℹ️ Taxa de perda mais baixa para indicações da faculdade (31.87%).
☝️ Indicações da faculdade performando melhor nesse gargalo. Indica um caminho de explorar ações de aquisição via caminho institucional (divulgação do Quero Bolsa na Faculdade).
C: Comprometido para D: Pago¶ℹ️ Taxa de perda mais alta para indicações de email (67.44%).
ℹ️ Taxa de perda mais baixa para indicações de amigo (13.97%).
☝️ Indicações de amigo com performance melhor na etapa final de fechamento. Pode ser resultado intermediário do programa Quero Pontos. A análise carece de informações para achar as teses de atuação e aprimoramento do programa.
segmented_paymment_funnels = display_segmented_funnels(
filtered_payment_orders_df,
'Etapa de checkout',
'Tipo do Pagamento'
);
paid_total = len(orders_df[orders_df['Etapa de checkout'] == 'D: Pago'])
paid_total
compromise_total = len(orders_df[orders_df['Etapa de checkout'] == 'C: Comprometido'])
compromise_total
compromise_total - paid_total
☝️ Numericamente, a quantidade de pagamentos efetuados explicitamente via Boleto é maior (3640 amostras) contra CreditCard (2583 amostras).
A informação de pagamento só é anotada na etapa
D: Pago, e não registrada nos passos anteriores. Há algumas formas de medir comparativamente e além do óbvio o quanto o boleto performa mais do que o cartão, mas seriam necessários outros datapoints.Pela amostra de dados, estimo cerca de 6344 boletos emitidos e não-pagos, baseado em informações da fase anterior
C: Comprometido.
| Meio | Intenções | Pagamentos | Conversão |
|---|---|---|---|
| Boleto | 9984 | 3640 | 36.45% |
| Cartão | 2583 | 2583 | 100.00% |
A possibilidade de adiamento do pagamento do boleto é uma vantagem ao cliente, mas ao mesmo tempo, dá margem para desistência ou arrependimento apesar do compromisso de fechamento. Isso pode responder por parte do volume de perdas.
Para o consumidor, a vantagem de parcelamento do cartão é muito atrativa, o que ajuda a diluir o custo da pré-matrícula, que é relativamente alto em função da mensalidade final.
segmented_course_type_funnels = display_segmented_funnels(
orders_df,
'Etapa de checkout',
'Modalidade do curso'
);
B: Registrado para C: Comprometido¶ℹ️ Taxa de perda mais alta para leads de Presencial (47.87%).
ℹ️ Taxa de perda mais baixa para leads da EaD (20.92%).
☝️ Investigar o que leva EaD a ter a menor taxa de perda nesse segmento, dado que a taxa de perda das outras modalidades estar mais próximas da média.
C: Comprometido para D: Pago¶ℹ️ Taxa de perda mais alta para leads de EaD (47.87%).
ℹ️ Taxa de perda mais baixa para leads da Semipresencial (45.23%).
☝️ Aqui a taxa de perda de EaD se move para algo mais próximo da média, com o semipresencial performando um pouco melhor.
segmented_course_level_funnels = display_segmented_funnels(
filtered_level_orders_df, 'Etapa de checkout', 'Nível do curso'
);
B: Registrado para C: Comprometido¶ℹ️ Taxa de perda mais alta para leads de Bacharelado (graduação) (47.45%).
ℹ️ Taxa de perda mais baixa para leads da Pós-graduação Lato Sensu (30.36%).
☝️ Estudante de graduação que se registra desistindo mais do que os outros estudantes nessa etapa.
C: Comprometido para D: Pago¶ℹ️ Taxa de perda mais alta para leads de Pós graduação Lato Senso (59.97%).
ℹ️ Taxa de perda mais baixa para leads da Bacharelado (graduação) (36.45%).
☝️ Observa-se a inversão de taxas entre as etapas (natural de progressão de funil). Encontrar as resistências mais comuns que estão fazendo o estudante de Pós desistir do compromisso.
segmented_uf_funnels = display_segmented_funnels(
filtered_campus_state_df,
'Etapa de checkout',
'Estado do campus'
);
B: Registrado para C: Comprometido¶ℹ️ Taxa de perda mais alta na praça de SP (53.85%).
ℹ️ Taxa de perda mais baixa na praça de MG (22.63%).
☝️ Praça de SP performando mal na conversão de
RegistradoparaComprometidoporém performando melhor no passo seguinte do funil (Pago).
C: Comprometido para D: Pago¶ℹ️ Taxa de perda mais alta na praça de AM (66.45%).
ℹ️ Taxa de perda mais baixa na praça de SP (34.44%).
☝️ Estado de SP apresentando melhor performance de conversão. Aqui valeria a pena refinar o lead por cidade dentro de São Paulo, para sentir variações entre o interior e capitais. Por conta do volume de leads, SP puxa o indicador para melhor performance. A atuação deve se dividir entre continuar aprimorando a performance de SP e também descobrir que estímulos existem em SP que podem ser replicados à outras praças.
☝️ Verificaria também a capilaridade presencial das ofertas nos estados que estão performando mal. Será que a distância entre a casa da pessoa e a faculdade tem sido um elemento que a leva a desistir da bolsa?
segmented_enem_funnels = display_segmented_funnels(
orders_df[orders_df['Ano que prestou ENEM'] > 0],
'Etapa de checkout',
'Ano que prestou ENEM'
);
B: Registrado para C: Comprometido¶ℹ️ Taxa de perda mais alta na safra de ENEM 2016 (52.96%).
ℹ️ Taxa de perda mais baixa na safra de ENEM 2010 (40.70%).
C: Comprometido para D: Pago¶ℹ️ Taxa de perda mais alta na safra de ENEM 2016 (71.65%).
ℹ️ Taxa de perda mais baixa nas safras de ENEM 2009 (17.24%).
☝️ A base de dados não deixa claro se a safra da amostra se refere à inscrições do mesmo ano/semestre ou se a amostra foi coletada de maneira distribuída no tempo. Não existe na amostra um campo para montagem de base temporal. Se as inscrições dos estudantes da amostra tiverem sido feitas na mesma época, há um indicativo que o estudante com a nota de ENEM mais recente está mais propenso a desistir. Há de se descobrir se existem fatores externos ao Quero Bolsa contribuindo para a quebra do funil nesse momento (71%), por exemplo, o estudante estar preenchendo o cadastro antes de obter plena informação sobre as opções que ele terá na mesa ou se matriculando diretamente na instituição.
O desafio oferece uma base de dados com o total de 107 dúvidas de usuários registradas. Esse número caiu para 102 amostras após remoção de registros duplicados.
O layout de dados disponibilizado foi:
| Coluna | Tipo de informação |
|---|---|
| Universidade | Texto |
| Curso | Texto |
| Pergunta | Texto |
Amostra da base de dados (cinco primeiros registros):
questions_df[['Universidade', 'Curso', 'Pergunta']].head()
Para análise textual, foi utilizada a base de stopwords do NLTK (Natural Language Toolkit), somado à uma base customizada de palavras. As "stopwords" são palavras que podem ser consideradas irrelevantes para o processamento da linguagem natural. Dessa forma, a base de dados ganhou uma nova coluna:
questions_df[['Pergunta', 'Pergunta NSW']].head()
Para exploração inicial, após a remoção das stopwords, foi gerado um diagrama de Wordcloud para explorar a frequência do conteúdo de dúvidas dos usuários:
wordcloud = WordCloud(
background_color='white',
max_words=500
).generate(questions_words)
plt.figure(
dpi=300,
figsize=(15,8)
)
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.title('Palavras')
plt.show()
☝️ A interpretação inicial da wordcloud a primeira vista evoca alguns sentimentos interesantes: A primeira palavra mais importante está relacionada ao destino acadêmico do aluno: O curso. Dúvidas sobre o que vai fazer. A segunda e terceira palavras coincidem com o nome do produto: quero bolsa.
A análise de colocações do NLTK apresenta o resultado a seguir:
text.collocations()
☝️Esse tipo de análise em processamento de linguagem natural indica expressões que consistem em mais de uma palavra, mas que podem dado um determinado contexto apresentar significado de palavras individuais.
Como a base amostral possui apenas cem registros, o NLKT produz algum resultado interessante. Mas é provável que o mesmo algoritmo rodando numa base completa vá identificar expressões comumente usadas pelos usuários para se referirem a coisas dentro do contexto do produto.
Exemplos de colocações:
quero bolsa, gostaria fazer, fazer curso - Expressão textual do desejo de estudar.
gostaria saber, saber posso - Expressão textual do desejo de saber mais sobre o Quero Bolsa.
curso técnico, curso direito, curso engenharia, curso gestão, curso engenharia - Expressão textual da existência de dúvidas técnicas sobre o curso de interesse.
bolsa curso, mensalidade curso - Expressão textual de preocupação sobre mensalidades.
distância curso - Expressão textual da necessidade de informações por modalidade de ensino.
O NLTK nos ajuda também a perceber a frequência de palavras dentro da amostra de questões:
plt.figure(figsize=(12,6))
text.vocab().plot(30, cumulative=False)
☝️ O gráfico acima mostra claramente uma Hierarquia de termos a serem explorados na UX do site, na redação da arquitetura (processo de labeling).
A execução desse mesmo tipo de análise com a amostra real irá dar dicas onde os termos aqui priorizados poderão ser usados para substituir sinônimos nos textos de conteúdo e títulos do site, aproximando a redação do Quero Bolsa aos termos a que os usuários estão mais acostumados.
O NLTK também oferece uma visão interessante sobre o contexto onde as palavras capturadas pela análise de frequência aparecem. Vamos aqui explorar alguns contextos:
word = 'curso'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
☝️Palavra curso insere-se nos contextos de:
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *10 ocorrências* |
| ```curso/custo``` | Custo de mensalidade. | *4 ocorrências* |
| ```curso/reputação``` | Aprovação pelo MEC e outros. | *3 ocorrências* |
| ```curso/modalidade``` | Modalidade presencial, semi-presencial, distância. | *2 ocorrências* |
| ```curso/tempo``` | Duração do curso. | *1 ocorrência* |
word = 'bolsa'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
☝️Palavra bolsa insere-se nos contextos de:
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *5 ocorrências* |
| ```quero/confiabilidade``` | Duvidas se o produto funciona. | *3 ocorrências* |
| ```quero/requisitos``` | Requisitos para conseguir a bolsa. | *3 ocorrências* |
| ```curso/custo``` | Custo de mensalidade. | *3 ocorrências* |
| ```quero/fluxo``` | Etapas para conseguir a bolsa. | *3 ocorrências* |
| ```curso/composição``` | Possibilidade de composição de bolsas. | *2 ocorrências* |
| ```quero/prazo``` | Prazo para conseguir uma bolsa (janela de tempo). | *2 ocorrências* |
| ```quero/requisitos``` | Requisitos para conseguir a bolsa. | *1 ocorrências* |
word = 'quero'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
word = 'gostaria'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
☝️Palavras quero e gostaria inserem-se nos contextos de:
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *12 ocorrências* |
| ```curso/custo``` | Custo de mensalidade. | *4 ocorrências* |
| ```quero/fluxo``` | Etapas para conseguir a bolsa. | *2 ocorrências* |
| ```curso/reputação``` | Aprovação pelo MEC e outros. | *2 ocorrências* |
| ```quero/confiabilidade``` | Duvidas se o produto funciona. | *1 ocorrência* |
| ```quero/requisitos``` | Requisitos para conseguir a bolsa. | *1 ocorrência* |
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *1 ocorrência* |
| ```curso/ementa``` | Grade disciplinar do curso. | *1 ocorrência* |
word = 'fazer'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
☝️Palavra fazer insere-se nos contextos de:
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *4 ocorrências* |
| ```quero/fluxo``` | Etapas para conseguir a bolsa. | *3 ocorrências* |
| ```quero/requisitos``` | Requisitos para conseguir a bolsa. | *3 ocorrências* |
| ```curso/custo``` | Custo de mensalidade. | *2 ocorrências* |
word = 'saber'
display(HTML('<h5>{}</h5>'.format(word)));
pure_text.concordance(word);
☝️Palavra saber insere-se nos contextos de:
| ```curso/custo``` | Custo de mensalidade. | *2 ocorrências* |
| ```curso/disponibilidade``` | Disponibilidade do curso no sistema. | *2 ocorrências* |
| ```quero/requisitos``` | Requisitos para conseguir a bolsa. | *1 ocorrência* |
| ```quero/fluxo``` | Etapas para conseguir a bolsa. | *1 ocorrência* |
| ```curso/reputação``` | Aprovação pelo MEC e outros. | *1 ocorrência* |
| ```curso/composição``` | Possibilidade de composição de bolsas. | *1 ocorrência* |
| ```curso/ementa``` | Grade disciplinar do curso. | *1 ocorrência* |
questions_df['Curso'].value_counts(sort=True, ascending=False)[0:10].plot.bar(figsize=[10,5]);
questions_df['Universidade'].value_counts(sort=True, ascending=False)[0:10].plot.bar(figsize=[10,5]);
Na amostra de usuários o maior volume de dúvidas está associado ao pilar curso/disponibilidade, um problema essencialmente de listing do estoque de parcerias da Quero Bolsa. O listing não está presente como assunto primário do Cabeçalho e da Seção principal do site: fica disponível indiretamente como resultado da pesquisa.
O title Bolsas de estudo de até 75% | Quero Bolsa possui uma amplitude de promessa muito grande. Afirma que é possível conseguir esse nível de redução na plataforma. A partir da análise da base de dados de ordens fica claro que a promessa é verdadeira e pode ser explorada.
bins=30
plt.figure(
figsize=(15,8)
)
plt.hist(
orders_df['Preço cheio no comprovante'].dropna().clip(lower=100, upper=2000),
bins=bins,
alpha=0.5,
rwidth=0.8,
label='Preço cheio'
)
plt.hist(
orders_df['Preço com desconto no comprovante'].dropna().clip(lower=100, upper=2000),
bins=bins,
alpha=0.5,
rwidth=0.8,
label='Preço com desconto'
)
plt.title('Distribuição por preço cheio / preço com desconto')
plt.legend(loc='upper right')
plt.show()
☝️Histograma da distribuição de preços proveniente da base de ordens. O deslocamento da distribuição da curva normal expressa o volume de descontos real que o Quero Bolsa oferece. O pico de desconto na base é de 80%, com desconto médio de 35%.
orders_df['Percentual de desconto'].describe()
Link para o pop de Como funciona é o elemento de abertura do site (barra superior), disponível como primeira opção na direção de leitura ocidental. A impressão que dá é que em versões passadas, poderia não estar claro para os usuários os aspectos de quero/confiabilidade (coletar sinais de que o Quero Bolsa realmente funciona) e quero/fluxo (me dar uma visão inicial de como a plataforma funciona). A versão atual abstrai em três passos, o que é uma prática legal do mercado do ponto de vista cognitivo por conta da gestão da expectativa do cliente.
A replicação dos 3 passos de como funciona em múltiplos momentos da plataforma deve ajudar na redução de pedidos associados a quero/fluxo.
A versão desktop deixa o box de como funciona escondido no menu, mas coloca a redundância dos passos logo abaixo do card promocional. A versão móvel comprime alguns tópicos do header em um novo menu de ajuda/info.
A logo Quero Bolsa aparece centralizada, o que é atípico, mas parece ser bom. Dá equilíbrio tanto na versão site quanto mobile.
O call to action de área logada com a chamada Entre e ative bolsas exclusivas parece ser um bom coletor de boca de funil.
☝️Exemplo de bom candidato para teste A/B. Parece meio obscuro e solto o argumento de entre e ative bolsas exclusivas.
ℹ️ A parte de ativação de bolsas exclusivas pressupõe ainda um conceito novo chamado "Nota quero". Esse conceito tá meio solto. Do ponto de vista de branding é uma coisa nova a aprender e que inverte a fluência narrativa. quem quer, quer algo (quero bolsa, quero educação). "Nota quero" subverte isso sem entregar vantagem. Esse espaço de interferência com elementos na marca deve ser protegido e só pode ser quebrado se isso retornar um valor significativo.
ℹ️ A coleta de dados para ativação de bolsas exclusivas é legal, mas ela abre um novo fluxo de três passos. Na base de dados, não está explicitado claramente
Explorações de design inserindo mutações e medições no cabeçalho nesse caso parece ser um fruto mais baixo de colher para a progressão dos indicadores.
O formulário de Encontre a sua bolsa de estudo é bastante interessante e está bem balanceado.
O site acertou a minha localização logo de partida, trazendo a minha cidade sensibilizada antes mesmo de usar o location services do navegador, o que é muito positivo.
☝️Para transferir para linguagem natural, testaria "Onde você mora?" ou "Qual a sua cidade?" em substituição a "Qual a sua localização".
Os controles de interface (select box, slider de preço) funcionam bem para desktop ou mobile.
☝️ O campo de "Prefere alguma faculdade?" faz a busca muito abrangente (lista todo o acervo de faculdades ou escolas). Na ordem provável de preenchimento, o sistema já sabe nesse momento em que localidade o usuário está. Para diminuir a chance de falha ao encontrar uma universidade por excesso de informação na caixa de auto-complete, o auto-complete poderia ser segregado entre faculdades próximas da localização do usuário (limite geométrico de uns 100 km, por exemplo) e a lista geral de instituições que atende aos caracteres digitados.
ℹ️ Os elementos de acessibilidade do formulário (principalmente tab index) parecem não estar funcionando bem. É difícil operar o formulário sem o uso de mouse. A janela de análise não permite testar a performance da plataforma em software ledor de tela.
A hipótese inicial para incentivo a parceiros para o aumento da quantidade e qualidade de informações oferecidas está ligada a identificar e suprir necessidades informacionais sobre o setor que sejam interessantes para a instuição. Testaria o argumento "me dá informações sobre a sua instituição e cursos" que você acessa aqui o painel consolidao sobre tudo o que está acontecendo e sendo ofertado na região onde você atual.
☝️ Um PM do Quero Bolsa precisa aprender como os tiers de parceiros (instituições) preenchem as vagas de um curso, desvendando o raciocínio do gestor educacional conforme porte, demanda e reputação da instituição, perspectiva de oferta na região da instituição. A elaboração de sugestões sem a entrada no domínio educacional é um exercício positivo de criatividade, mas caracteriza inversão do fluxo de trabalho.
Para instituições com problema de preenchimento de vagas/alunos, a construção de indicadores claros sobre as informações de cursos, reserva de vagas, etc.. Pode também funcionar como elemento estimulante, com sistematização de KPIs semelhantes à profile completeness, porém adaptados para o preenchimento correto da página do curso, elemento fundamental na conversão.
O https://avaliacao.querobolsa.com.br/ (Avaliação Quero Bolsa) parece ser um elemento menos maduro na plataforma do Quero Bolsa.
☝️ O conceito Nota Quero / Fazer minha Nota Quero poderia ser trabalhado na perspectiva de produto, em termos de naming, branding e também design da plataforma: hoje tem um aspecto parecido com Google Forms, distoante da plataforma. A principal ferramenta de coleta direta de dados não conversa direito com o restante do produto.
Um grande desafio também é a conciliação das jornadas entre os usuários que se matriculam pelo listing normal versus alcance de bolsa exclusiva.
☝️ Não possuo um número de ENEM válido para ultrapassar o passo "2. Informe sua nota do Enem".
Na página dos cursos, é interessante também observar o volume de avaliações do ponto de vista de quem está estudando, explorando pontos positivos e negativos. Isso coloca a plataforma numa posição de transparência em relação à instituição.
☝️ O estímulo aos usuários na postagem de avaliações passa pela régua de comunicação ativando bolsistas em curso. O programa de refferal apresenta incentivo interessante para o bolsista corrente ganhar desconto em mensalidades. As informações do usuário (alunos) se desdobram nesses dois caminhos: o usuário dentro do funil e o usuário que já passou pelo funil e pode contribuir com a plataforma para novos usuários e enriquecimento da oferta de cursos com testemunhos.
Após realização de teste A/B para testar a performance da tese, realizar eliminação gradativa do boleto como meio de pagamento, começando pelos cursos de mensalidades com preços menos populares. A ideia é diminuir o gargalo de C: Confirmado para D: Pago com o processamento instantâneo do pagamento da matrícula.
Usar repertório de atividades de UX para aprimorar os módulos de busca de cursos e detalhamento de curso para incorporar, hierarquizar ou modificar apresentação de itens comuns que foram mapeados na análise das dúvidas dos usuários.
Pelo perfil da base atual de originação recebida, avaliaria testar execução de campanhas de referral com YouTubers e streamers que conversem com o target de audiência da Quero Educação. A gente que está de fora não sabe se isso já foi testado pois provavelmente estou fora da audiência a que a comunicação está sendo direcionada.
A ideia é testar modelos de crescimento baseados em remuneração por código promocional (ganho maior pós-matrícula). Esses novos canais estão ocupando os espaços de influência nas novas gerações. A marca Quero Bolsa acaba sendo apresentada a estudantes ainda no início do ensino médio, para que comecem a entender a plataforma como parte essencial de quando forem escolher a sua faculdade, com reflexo de branding a médio-longo prazo.
A análise pode dispor de mais informações para melhorar a qualidade das análises. São elas:
O tempo dos testes em cinco dias corridos torna muito difícil o aprofundamento nas questões. O candidato precisa escolher os temas onde vai se aprofundar para performar bem na seleção, contudo, atrapalha a homogeneidade do teste. O grau de maturidade da plataforma e a quantidade de caminhos possíveis para o usuário completar a jornada de se matricular em um curso adiciona pontos intensivos em tempo de análise, junção de fontes de dados, observação do produto, engajamento com o time e conversas com designers, programadores, time de produto e demais pessoas responsáveis.
A base de questões em torno de 100 pontos amostrais para a base de questões aparenta não permitir inferências ou extrapolação de conclusões.
A disponibilização dos acessos de Analytics para o site é de grande utilidade para o Gerente de Produto. Várias dimensões de análise são adicionadas, especialmente o tipo de dispositivo (desktop ou móvel), os dados de localidade, as origens de acesso e o perfil de acesso dos visitantes.
Ferramentas como o Jupyter podem ser instaladas na mesma VPC dos servidores de produção para acessar as bases de dados diretamente e produzir consultas que podem otimizar o levantamento dos indicadores de produto sem onerar os times de tecnologia.
A análise textual das interações com o WhatsApp alimentam informações para contribuição em outros canais, e de onde pode-se extrair também modificações de tendência em tempo real, permitindo sentir eventos exógenos de mercado e legislação que possam interferir com o funil de aquisição.
Dentro do pool de canais do Quero Bolsa a obtenção de mais informações sobre o 0800 ajudaria a entender melhor a importância do canal, dando visada da taxa de conversão no canal, a duração do ciclo de vendas (tempo médio), visada de cadência das ligações, apuração de entendimento da sazonalidade do setor, dos padrões em torno dos dias da semana e das horas do dia. Buscaria entender também como funciona o canal 0800 em termos de adicionar confiabilidade para a plataforma.
Mapeamento completo e estabelecimento de rotina de atualização de benchmark da concorrência direta do Quero Bolsa dividido por canal (conteúdo e abordagem de canal) e também a parte de abordagem de comunicação (peças, réguas, filmes). Por um lado, é papel do Quero Bolsa inovar e ser seguido. Por outro lado, eventuais boas ideias sobre problemas endereçados pelo setor surgirão, para serem avaliadas precocemente e, se for o caso, incorporada nos produtos. Limitações de tempo para execução do teste impedem a execução de benchmark.
O histórico de evolução do Quero Bolsa é muito valioso para desvendar soluções atuais que possam ter resolvido problemas conhecidos no passado.